from AStock.ASEvent import Worker
from AStock.ASSetting import settings, cons
from AStock.ASUtil import ASHttpParser, date_to_bar_format, date_to_timestamp


class ASStockDayCrawler(Worker):

    def __init__(self):
        super(ASStockDayCrawler, self).__init__()
        self._proxy = None
        self._handler = None
        self._code_mapping = None
        if settings.stock_day_source == cons.CrawlSource.TENCENT:
            self._url = cons.TENCENT_STOCK_DAY
            self._handler = self._tencent_text_handler
            self._code_mapping = self._tencent_code_mapping
        else:
            raise NotImplementedError

    @staticmethod
    def _tencent_code_mapping(code):
        return 'sh' + code if code[0] == '6' else 'sz' + code

    @staticmethod
    def _tencent_text_handler(text, **kwargs):
        """
        解析响应的文本
        :param text: str，待解析的响应文本，以 ~ 分割字符串，格式如下：
                    v_sh600519="1~贵州茅台~600519~358.74~361.29~361.88~27705~12252~15453~358.75~8~358.74~4~358.72~7~358.71~6~358.70~5~358.77~3~358.78~2~358.79~16~358.80~4~358.86~
                    1~14:59:59/358.75/5/S/179381/28600|14:59:56/358.75/1/S/35875/28594|14:59:53/358.75/1/S/35875/28588|14:59:50/358.75/1/S/35875/28579|14:59:47/358.75/4/B/143499/28574|14:59:41/358.72/4/S/143501/28562~20170221150553~
                    -2.55~-0.71~362.43~357.18~358.75/27705/994112865~27705~99411~0.22~27.24~~362.43~357.18~1.45~4506.49~4506.49~6.57~397.42~325.16~0.86";

                    下标从0开始，依次为：
                     1  0: 未知
                     2  1: 股票名字
                     3  2: 股票代码
                     4  3: 当前价格
                     5  4: 昨收
                     6  5: 今开
                     7  6: 成交量（手）
                     8  7: 外盘
                     9  8: 内盘
                    10  9: 买一
                    11 10: 买一量（手）
                    12 11-18: 买二 买五
                    13 19: 卖一
                    14 20: 卖一量
                    15 21-28: 卖二 卖五
                    16 29: 最近逐笔成交
                    17 30: 时间
                    18 31: 涨跌
                    19 32: 涨跌%
                    20 33: 最高
                    21 34: 最低
                    22 35: 价格/成交量（手）/成交额
                    23 36: 成交量（手）
                    24 37: 成交额（万）
                    25 38: 换手率
                    26 39: 市盈率
                    27 40:
                    28 41: 最高
                    29 42: 最低
                    30 43: 振幅
                    31 44: 流通市值 （亿）
                    32 45: 总市值 （亿）
                    33 46: 市净率
                    34 47: 涨停价
                    35 48: 跌停价
        :return: dict，
            失败时，None
            成功时：
                {
                    "code" : 300003,
                    "open" : 29.3,
                    "close" : 28.0,
                    "high" : 29.3,
                    "low" : 28.0,
                    "vol" : 1918.0,  # 成交量 （手）
                    "amount" : 5370400.0,  # 成交额 （元）
                    "date" : "1991-12-26",
                    "date_stamp" : 693676800.0, # 1991-12-26 00:00:00
                    "total_mv" : 123456,  # 总市值 （元）
                    "circ_mv" : 123000,  # 流通市值 （元）
                    "turnover_rate" : 1.2,  # 换手率（%）
                    "pre_close" : 28.3,  # 昨收价
                    "change" : 3, # 涨跌额
                    "pct_chg" : 0.2  # 涨跌幅 （未复权）（%）
                }
        """
        items = text.split('"')
        if len(items) != 3:
            print('error parse tencent stock_day: {}'.format(text))
            return None
        text = items[1]

        items = text.split('~')
        if len(items) < 45:
            print('error parse tencent stock_day: {}'.format(text))
            return None
        res = {
            "code": items[2],
            "open": float(items[5]),
            "close": float(items[3]),
            "high": float(items[33]),
            "low": float(items[34]),
            "vol": float(items[36]),
            "amount": float(items[37]) * 10000,
            "date": date_to_bar_format(items[30][:len('20200901')]),
            "date_stamp": date_to_timestamp(items[30][:len('20200901')]),
            "total_mv": float(items[45]) * 100000000,
            "circ_mv": float(items[44]) * 100000000,
            "turnover_rate": float(items[38]),
            "pre_close": float(items[4]),
            "change": float(items[31]),
            "pct_chg": float(items[32])
        }
        return res

    def run(self, event_data):
        """
        执行请求日数据的任务
        :param event_data: dict，包括：
            event_type: stock_day
            code: 300003

        :return: dict，
            失败时，返回的字典为 {'error': 'error string'}
            成功时，返回的字典为：
                {
                    "code" : 300003,
                    "open" : 29.3,
                    "close" : 28.0,
                    "high" : 29.3,
                    "low" : 28.0,
                    "vol" : 1918.0,  # 成交量 （手）
                    "amount" : 5370400.0,  # 成交额 （元）
                    "date" : "1991-12-26",
                    "date_stamp" : 693676800.0, # 1991-12-26 00:00:00
                    "total_mv" : 123456,  # 总市值 （亿）
                    "circ_mv" : 123000,  # 流通市值 （亿）
                    "turnover_rate" : 1.2,  # 换手率（%）
                    "pre_close" : 28.3,  # 昨收价
                    "change" : 3, # 涨跌额
                    "pct_chg" : 0.2  # 涨跌幅 （未复权）（%）
                }
        """
        try:
            event_type = event_data['event_type']
            code = event_data['code']
        except KeyError as e:
            return {'error': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['stock_day']:
            return {'error': 'event type error: {}'.format(event_type)}

        if self._code_mapping:
            code = self._code_mapping(code)

        url = self._url.format(code=code)
        parser = ASHttpParser(url, cons.HEADER, self._proxy, content_type="text")
        res = parser.parse_text(text_handler=self._handler)
        if res is None:
            return {'error': 'error crawl stock day, url: {}'.format(url)}
        return res


class ASStockDaySaver(Worker):

    def __init__(self):
        super().__init__()

    def run(self, event_data):
        """
        执行保存日数据的任务
        :param event_data: dict，包括：
            event_type: stock_day
            data: 日数据，dict，
                {
                    "code" : 300003,
                    "open" : 29.3,
                    "close" : 28.0,
                    "high" : 29.3,
                    "low" : 28.0,
                    "vol" : 1918.0,  # 成交量 （手）
                    "amount" : 5370400.0,  # 成交额 （千元）
                    "date" : "1991-12-26",
                    "date_stamp" : 693676800.0, # 1991-12-26 00:00:00
                    "total_mv" : 123456,  # 总市值
                    "circ_mv" : 123000,  # 流通市值
                    "turnover_rate" : 12,  # 换手率
                    "pre_close" : 28.3,  # 昨收价
                    "change" : 3, # 涨跌额
                    "pct_chg" : 0.2  # 涨跌幅 （未复权）
                }
        :return: dict，
            成功：{'succ': 'saved: stock day 300003'}
            失败：{'fail': 'failed to save: stock day 300003'}
        """
        try:
            event_type = event_data['event_type']
            data = event_data['data']
        except KeyError as e:
            return {'fail': 'key error in event data, key: {}'.format(e)}

        if event_type not in ['stock_day']:
            return {'fail': 'event type error: {}'.format(event_type)}

        succ = 'saved: stock day {}'.format(data['code'])
        fail = 'failed to save: stock day {}'.format(data['code'])

        try:
            coll = settings.database.stock_day  # 文档集合对象
            coll.update_one({'code': data['code'], 'date': data['date']}, {'$set': data}, upsert=True)
        except Exception as e:
            return {'fail': fail + '    ' + str(e)}

        return {'succ': succ}
